This document is provided by the WebWork user community and does not represent the supported Spring integration methods. Please refer to Spring for documentation on the recommended integration.

I started out using the original WebWork documentation to get Spring to initialize Webwork actions, but it appears that a lot has changed since the days of WebWork 1.x. This will be my attempt to clarify some of those changes and to list the steps necessary to get the two to play nicely. Please comment with clarifications or corrections!

WebWork 1.x

(these are assumptions based on the 1.x WW1:Spring Framework Integration documentation)
It seems that the way you got Spring to initialize WebWork 1.x action classes was to add this line into your webwork.properties file:

webwork.action.factory=webwork.action.factory.SpringActionFactory

so that WebWork would know to use Spring's view of the world to create actions. The WebWork action classes would then need to be declared in the Spring applicationContext.xml file so that Spring would know directly of the action objects. Upon invocation of an action, WebWork would know to first use the SpringActionFactory to try and create an instance of the requested action which would ask Spring to create the object using its configuration. If there was no Spring definition of that action object, then WebWork would use it's normal instantiation methods to create that action. Well, things have changed slightly since WebWork 1.x.

WebWork 2

In WebWork 2 (the functionality actually exists in XWork), you specify relationships from action classes to other objects in XWork's xwork.xml file instead of Spring's applicationContext.xml file. So if you have an action class that utilizes a DAO, instead of having a bean definition like so in applicationContext.xml:

<bean id="myAction" class="com.ryandaigle.web.actions.MyAction" singleton="false">
	<property name="DAO">
		<ref bean="myDAO"/>
	</property>
<bean id="myDAO" class="com.ryandaigle.persistence.MyDAO" singleton="true" />

you move the action definition to xwork.xml and keep the DAO definition in applicationContext.xml so that xwork.xml looks like:

<action name="myAction" class="com.ryandaigle.web.actions.MyAction">
	<external-ref name="DAO">myDAO</external-ref>
	<result name="success" type="dispatcher">
		<param name="location">/success.jsp</param>
	</result>
</action>

and applicationContext.xml looks like:

<bean id="myDAO" class="com.ryandaigle.persistence.MyDAO" singleton="true" />

Notice how there is the external-ref element in the action definition that points to an object that Spring is managing. There are several things that need to be in place for the external-ref to work, but I just wanted to give an overview of what has changed before going into the specific steps.

Steps for Configuring Spring/WebWork2 (XWork) Integration:

Get the files you need to externally resolve Spring beans. I've bundled them all here: http://www.ryandaigle.com/pebble/images/webwork2-spring.jar . They were originally spread between two JIRA issues filed against XWork 1.0 (see references below). This zip includes the source, the class files (so you can just include it in your classpath) and my example configuration files. Either extract the source files into your application, or put the file onto your classpath. (You may want to take the applicationContext.xml and xwork.xml files out, I don't know if they'll override your files... They're just there as an example configuration).

Now, let's get your XWork configuration file (xwork.xml) to resolve external references. XWork resolves external references (using the external-ref element) by utilizing an external reference resolver per package. You specify your external reference resolver as an attribute of the package element:

<package name="default" extends="webwork-default"
	externalReferenceResolver="com.atlassian.xwork.ext.SpringServletContextReferenceResolver">

This SpringServletContextReferenceResolver class reference is a class not part of the XWork distribution written as an extensions for XWork/Spring that I got from this JIRA issue filed against XWork addressing this Spring integration effort. (I have bundled it with the rest of the necessary files later on down for your convenience). This class will intercept all external-refs and resolve the references using Spring's context. There is also a SpringApplicationContextReferenceResolver included in the zip file that will allow you to resolve Spring references for applications not executing within the web context. But as this is a WebWork/Spring article, the servlet resolver is what we need to use.

Now we need to add the XWork reference resolver as part of the interceptor stack you're using. This will allow any references to be resolved (using the reference resolver you specified in the externalReferenceResolver attribute). This is how I've added that interceptor:

<interceptors>
	<interceptor name="reference-resolver" class="com.opensymphony.xwork.interceptor.ExternalReferencesInterceptor"/>
	<interceptor-stack name="myDefaultWebStack">
		<interceptor-ref name="defaultStack"/>
		<interceptor-ref name="reference-resolver"/>
	</interceptor-stack>
</interceptors>
<default-interceptor-ref name="myDefaultWebStack"/>

As I briefly outlined before, you can now reference Spring beans that your action classes need in xwork.xml:

<action name="myAction" class="com.ryandaigle.web.actions.MyAction">
	<external-ref name="DAO">myDAO</external-ref>
	<result name="success" type="dispatcher">
		<param name="location">/success.jsp</param>
	</result>
</action>

And that's all we have to do to xwork.xml to let XWork know how to resolve references to Spring's managed beans.

Now let's setup our web environment to properly notify Spring and our external reference resolver of the web context. We do this by adding two context listeners to your application's web.xml file:

<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
	<listener-class>com.atlassian.xwork.ext.ResolverSetupServletContextListener</listener-class>
</listener>

The first listener is Spring's that you would need independent of whether or not you were integrating with WebWork 2. The second listener is our external resolver's that will use the servlet context to retrieve Spring's application context. This is the link between WebWork and Spring.
At this point, we've set up Spring and our XWork reference resolver to work within a web context, and we've told XWork how to resolve external references to Spring. We're done! Fire it up and let me know if there are some steps I've missed or assumptions I've made that I shouldn't have.

References

  • Here is my bundled source, class and example configuration files (that contains all the needed referenced files below); http://www.ryandaigle.com/pebble/images/webwork2-spring.jar .
  • My searching started with the original WebWork 1.x + Spring documentation and comments on the Wiki; WW1:Spring Framework Integration
  • The Wiki pointed me to the two JIRA issues that contained the source files for the reference resolvers:
  • http://jira.opensymphony.com/browse/XW-122 The "SpringExternalResolver.zip" attachment is the one needed for externally resolving Spring objects.
  • http://jira.opensymphony.com/browse/XW-132 The "xwork-springServletImpl.zip" attachment is the one needed for externally resolving Spring objects. It just contains some files missing from the original source.

    Credits

    Judging by the comments etc... of the JIRA issues filed against XWork, it appears that Ross Mason (of Atlassian?) is the man to thank for the external reference resolver code. And of course we have to thank the people of Spring and WebWork 2 for making this all possible.

Using the SpringObjectFactory

Rather than using an external reference resolver with releases of XWork from 1.0.1 and onwards, it's possible to use the SpringObjectFactory from the xwork-optional" package. This uses Spring to wire up the dependencies for an Action before passing it to XWork. Each action should be configured within a Spring application context as a prototype (because XWork assumes a new instance of a class for every action invocation):

<bean name="some-action" class="fully.qualified.class.name" singleton="false">
    <property name="someProperty"><ref bean="someOtherBean"/></property>
</bean>

Within xwork.xml:

<action name="myAction" class="some-action">
    <result name="success">view.jsp</result>
</action>

Notice that the XWork Action's class name is the bean name defined in the Spring application context.

The 1.1.3 release of the Spring/XWork integration library allows the user to configure everything in the xwork.xml file without needing to add extra entries to the applicationContext.xml. This is done by configuring the actions with the fully qualified class name (as if not using the SpringObjectFactory) It also added the ability to make use of constructor-based dependency injection without any further changes. The major caveat when using constructor-based DI is that objects passed in to the constructor must be unambiguous within the applicationContext (as is normally required by Spring) If there is any ambiguity, then you can still configure things the more traditional way, splitting the configuration of the action between xwork.xml and applicationContext.xml as described above.

One other advantage of the SpringObjectFactory approach is that it can also be used to load interceptors using the same sort of logic. If the interceptor is stateless, then it's possible to create the interceptor as a singelton instance, but otherwise it's best to create it as a Spring prototype.

In order to be used, the default ObjectFactory that XWork uses should be replaced with an instance of the SpringObjectFactory. The xwork-optional package ships with a ContextListener that does this, assuming that the Spring application context has already been configured.

ActionAutowiringInterceptor

Another alternative to using the SpringObjectFactory is to use the ActionAutowiringInterceptor. The interceptor will autowire any action class based on the autowire strategy defined. An advantage to using the interceptor over the SpringObjectFactory is that the action classes do not have to defined in the Spring's application context. The following is an example of how it can be configured in xwork.xml:

<interceptors>
  <interceptor name="autowire" class="com.opensymphony.xwork.spring.interceptor.ActionAutowiringInterceptor">
    <param name="autowireStrategy">1</param>
  </interceptor>
  <interceptor-stack name="autowireDefault">
    <interceptor-ref name="autowire"/>
    <interceptor-ref name="defaultStack"/>
  </interceptor-stack>
</interceptors>

Note the the autowireStrategy parameter is optional. If you do not define it, then the SpringObjectFactory will default to autowiring by name. The interceptor looks for Spring's application context in the XWork's application context. To initialize the application context, add the following listener to your web.xml:

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

You do not have to configure the SpringObjectFactory seperately unless you plain on instantiating results, interceptors, or validators as Spring beans. As a convenience method to get access to the application context for other uses, it is placed in the ActionContext map under the key ActionAutowiringInterceptor.APPLICATION_CONTEXT for each Action.